Tutorial: Running populations with binary_c-python with a source file
This notebook will show you how to evolve a population of stars through a source file that contains a set of pre-determined systems.
To enable source file sampling we need to configure the population object with evolution_type="source_file"
and we need to provide a filename that points to the source file, i.e. source_file_sampling_filename=source_file_sampling_filename
[1]:
import os
from binarycpython.utils.functions import temp_dir, output_lines
from binarycpython import Population
TMP_DIR = temp_dir("notebooks", "notebook_source_file", clean_path=True)
We will first set up a Population object with the correct configuration for source file sampling, and add some parsing function and a custom logging routine. Please note, the custom logging and parsing of the output of binary_c is currently a very simple example. Actual use-cases are more complex in their data handling. They are merely intended to support the show-case for source file sampling.
[2]:
source_file_pop = Population(
tmp_dir=TMP_DIR,
evolution_type="source_file",
num_cores=1
)
[3]:
# Create custom logging statement
custom_logging_code = """
Printf("EXAMPLE_SOURCE_FILE_LOGGING %30.12e %g %g %g %d\\n",
//
stardata->model.time, // 1
stardata->star[0].mass, // 2
stardata->common.zero_age.mass[0], // 3
stardata->model.probability, // 4
stardata->star[0].stellar_type // 5
);
"""
source_file_pop.set(
C_logging_code=custom_logging_code
)
[4]:
def parse_function(self, output):
"""
Example parse function
"""
parameters = ["time", "mass", "zams_mass", "probability", "stellar_type"]
# Go over the output.
for line in output_lines(output):
headerline = line.split()[0]
# CHeck the header and act accordingly
if headerline == "EXAMPLE_SOURCE_FILE_LOGGING":
values = line.split()[1:]
# Check if the length matches the expected length
if not len(parameters) == len(values):
print("Number of column names isnt equal to number of columns")
raise ValueError
# print some info
value_dict = {key: float(value) for key, value in zip(parameters, values)}
# To prevent filling the notebook with each timestep, lets just print one thing at the end. The purpose of this example is to show how things work.
print(value_dict)
# Add the parsing function
source_file_pop.set(
parse_function=parse_function,
)
File content/format
The sampling from source file method allows for two different types of files. The choice for the type of file is controlled via the option source_file_sampling_type
, and the options are command
and column
command based
This is type of source file should contain lines that are like command line commands for binary_c
, i.e. a sequence of key + value pairs, optionally prepended by binary_c
, e.g.:
binary_c M_1 10 M_2 5 orbital_period 10000
binary_c M_1 1 M_2 0.5 orbital_period 1000 metallicity 0.001
[5]:
# create an example source file with systems.
example_command_based_sourcefile = os.path.join(TMP_DIR, 'example_command_based_sourcefile.txt')
with open(example_command_based_sourcefile, 'w') as f:
f.write("""binary_c M_1 10 M_2 5 orbital_period 10000
binary_c M_1 1 M_2 0.5 orbital_period 1000 metallicity 0.001""")
# Run the population
source_file_pop.set(
source_file_sampling_filename=example_command_based_sourcefile,
source_file_sampling_type='command'
)
# evolve population
source_file_pop.evolve()
setting up the system_queue_filler now
Loading source file from /tmp/binary_c_python-david/notebooks/notebook_source_file/example_command_based_sourcefile.txt
Source file loaded
Signalling processes to stop
{'time': 15000.0, 'mass': 1.33478, 'zams_mass': 10.0, 'probability': 1.0, 'stellar_type': 13.0}
{'time': 15000.0, 'mass': 0.602425, 'zams_mass': 1.0, 'probability': 1.0, 'stellar_type': 11.0}
****************************************************
* Process 0 finished: *
* generator started at 2023-05-18T17:57:54.917371 *
* generator finished at 2023-05-18T17:57:55.331367 *
* total: 0.41s *
* of which 0.36s with binary_c *
* Ran 2 systems *
* with a total probability of 2 *
* This thread had 0 failing systems *
* with a total failed probability of 0 *
* Skipped a total of 0 zero-probability systems *
* *
****************************************************
**********************************************************
* Population-5407e244c9ee45b2848f4cb2dce32b03 finished! *
* The total probability is 2. *
* It took a total of 0.80s to run 2 systems on 1 cores *
* = 0.80s of CPU time. *
* Maximum memory use 170.023 MB *
**********************************************************
No failed systems were found in this run.
[5]:
{'population_id': '5407e244c9ee45b2848f4cb2dce32b03',
'evolution_type': 'source_file',
'failed_count': 0,
'failed_prob': 0,
'failed_systems_error_codes': [],
'errors_exceeded': False,
'errors_found': False,
'total_probability': 2,
'total_count': 2,
'start_timestamp': 1684429074.8716478,
'end_timestamp': 1684429075.6765742,
'time_elapsed': 0.8049263954162598,
'total_mass_run': 16.5,
'total_probability_weighted_mass_run': 16.5,
'zero_prob_stars_skipped': 0}
Alright. That worked well! Please note that some of the analytics dict output is not valid/appropriate here (e.g. total_probability_weighted_mass_run
) because we do not use actual probability distribution functions.
Let’s try the column based sampling next.
Column based
This type of source file should start with a header line that indicates which parameter is stored in which header. The subsequent lines should only contain the values of the corresponding parameters, e.g.:
M_1 M_2 orbital_period
10 5 1
1 0.5 1000
[6]:
# create an example source file with systems.
example_command_based_sourcefile = os.path.join(TMP_DIR, 'example_command_based_sourcefile.txt')
with open(example_command_based_sourcefile, 'w') as f:
f.write("""M_1 M_2 orbital_period
2 1 1
0.7 0.5 1000""")
# Run the population
source_file_pop.set(
source_file_sampling_filename=example_command_based_sourcefile,
source_file_sampling_type='column'
)
# evolve population
source_file_pop.evolve()
setting up the system_queue_filler now
Loading source file from /tmp/binary_c_python-david/notebooks/notebook_source_file/example_command_based_sourcefile.txt
Source file loaded
Signalling processes to stop
{'time': 15000.0, 'mass': 0.680111, 'zams_mass': 2.0, 'probability': 1.0, 'stellar_type': 11.0}
{'time': 15000.0, 'mass': 0.7, 'zams_mass': 0.7, 'probability': 1.0, 'stellar_type': 0.0}
****************************************************
* Process 0 finished: *
* generator started at 2023-05-18T17:57:55.737947 *
* generator finished at 2023-05-18T17:57:56.064031 *
* total: 0.33s *
* of which 0.27s with binary_c *
* Ran 2 systems *
* with a total probability of 2 *
* This thread had 0 failing systems *
* with a total failed probability of 0 *
* Skipped a total of 0 zero-probability systems *
* *
****************************************************
**********************************************************
* Population-680d1fe0533f4e6f9ce1ea5794b07e9b finished! *
* The total probability is 2. *
* It took a total of 0.72s to run 2 systems on 1 cores *
* = 0.72s of CPU time. *
* Maximum memory use 172.641 MB *
**********************************************************
No failed systems were found in this run.
[6]:
{'population_id': '680d1fe0533f4e6f9ce1ea5794b07e9b',
'evolution_type': 'source_file',
'failed_count': 0,
'failed_prob': 0,
'failed_systems_error_codes': [],
'errors_exceeded': False,
'errors_found': False,
'total_probability': 2,
'total_count': 2,
'start_timestamp': 1684429075.7047536,
'end_timestamp': 1684429076.4274688,
'time_elapsed': 0.7227151393890381,
'total_mass_run': 4.2,
'total_probability_weighted_mass_run': 4.2,
'zero_prob_stars_skipped': 0}
That works fine too!